﻿using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Ude;
using System.Text.RegularExpressions;
using Simple_search.Service;
using System.Windows.Navigation;
using System.Windows.Controls;

namespace Simple_search.Data
{
    public interface IBook: IDisposable
    {
        public string Name { get; }
        public string FilePath { get; }
        public bool IsOpen { get; set; }
        public int BlockSize { get; }

        public int GetBlockId(int position);
        public List<SearchResult> Search(Regex pattern);
        public string GetBlockContent(int startBlockId, int endBlockId);
        public IBook Open();
    }

    public class BaseBook: IDisposable
    {
        public BaseBook(string filePath, int blockSize) { 
            FilePath = filePath;
            Name = Path.GetFileNameWithoutExtension(filePath);
            BlockSize = blockSize;
        }

        public string Name { get; }
        public string FilePath { get; }
        public bool IsOpen { get; set; } = false;
        public int BlockSize { get; }
        protected string Content { get; set; } = string.Empty;

        public int GetBlockId(int position) => position / BlockSize;

        public string GetBlockContent(int startBlockId, int endBlockId)
        {
            if (IsOpen)
            {
                int start = BlockSize * startBlockId;
                int end = BlockSize * (endBlockId + 1);
                end = end <= Content.Length ? end : Content.Length;
                return Content.Substring(start, end - start);
            } else
            {
                throw new Exception("book not opened");
            }    
        }

        public void Dispose()
        {
            Content = string.Empty;
            IsOpen = false;
        }
    }

    public class EpubBook : BaseBook, IBook
    {
        public EpubBook(string filePath, int blockSize) : base(filePath, blockSize)
        {
        }
        
        public List<SearchResult> Search(Regex pattern)
        {
            List<SearchResult> results = new List<SearchResult>();
            if (IsOpen)
            {
                foreach (Match match in pattern.Matches(this.Content))
                {
                    SearchResult searchResult = new(this, match.Index, match.Length);
                    results.Add(searchResult);
                }
            }
            else
            {
                this.Open();
                foreach (Match match in pattern.Matches(this.Content))
                {
                    SearchResult searchResult = new(this, match.Index, match.Length);
                    results.Add(searchResult);
                }
                this.Dispose();
            }
            return results;
        }

        public IBook Open()
        {
            Content = EpubReader.Read(FilePath);
            IsOpen = true;
            return this;
        }

    }


    public class TextBook : BaseBook, IBook
    {
        public TextBook(string filePath, int blockSize, string encodeName) : base(filePath, blockSize)
        {
            Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
            Encode = Encoding.GetEncoding(encodeName);
        }
        
        private Encoding Encode { get; }

        public static string GetEncode(string filePath)
        {
            using (FileStream stream = File.OpenRead(filePath))
            {
                CharsetDetector detector = new CharsetDetector();
                detector.Feed(stream);
                detector.DataEnd();
                string result = detector.Charset;
                if (result is null)
                {
                    throw new Exception("Can't detect encode");
                }
                else
                {
                    return result;
                }
            }
        }
        
        public List<SearchResult> Search(Regex pattern)
        {
            List<SearchResult> results = new List<SearchResult>();
            if (IsOpen)
            {
                foreach (Match match in pattern.Matches(this.Content))
                {
                    SearchResult searchResult = new(this, match.Index, match.Length);
                    results.Add(searchResult);
                }
            }
            else
            {
                this.Open();
                foreach (Match match in pattern.Matches(this.Content))
                {
                    SearchResult searchResult = new(this, match.Index, match.Length);
                    results.Add(searchResult);
                }
                this.Dispose();
            }
            return results;
        }

        public IBook Open()
        {
            using FileStream stream = File.OpenRead(FilePath);
            using StreamReader streamReader = new StreamReader(stream, Encode);

            string originString = streamReader.ReadToEnd();
            Regex remove = new(@"[\u0002|\u0003|\u0000]");
            Content = remove.Replace(originString, string.Empty);
            IsOpen = true;
            return this;
        }
    }
}
